#include <string>
#include <cstring>
#include <cassert>
#include <fcntl.h>
#include <unistd.h>
#include "utils.hpp"
using namespace std;

#define SRCW_FIFO "srcw_fifo"
#define SWCR_FIFO "swcr_fifo"

const int SERVER = 1;
const int CLIENT = 0;

const int SRCW = 1;
const int SWCR = 0;

class syncShm
{
public:
    syncShm(int type, int mode = SRCW)
        : _type(type), _shmid(0), _addr(0), _rfd(0), _wfd(0), _begin_to_write(true)
    {
        if (mode == SRCW)
        {
            if (type == SERVER)
            {
                _shmid = createShm();
                createFifo(SRCW_FIFO);
                createFifo(SWCR_FIFO);

                _wfd = openFifo(SWCR_FIFO, O_WRONLY);
                _rfd = openFifo(SRCW_FIFO, O_RDONLY);
            }
            else
            {
                _shmid = getShm();
                _rfd   = openFifo(SWCR_FIFO, O_RDONLY);
                _wfd   = openFifo(SRCW_FIFO, O_WRONLY);
            }
        }
        else
        {
        }

        _addr = attachShm(_shmid);
        assert(_addr != nullptr);
    }

    ~syncShm()
    {
        detachShm(_addr);
        if (_type == SERVER)
        {
            deleteShm(_shmid);
            unlink(SWCR_FIFO);
            unlink(SRCW_FIFO);
        }
    }

    char* getAddr()
    {
        return _addr;
    }

    void write(std::string& input)
    {
        if (_type == CLIENT && _begin_to_write)
        {
            strcpy(_addr, input.c_str());
            notify(_wfd);
            _begin_to_write = false;
            return;
        }

        if (wait(_rfd))
        {
            if (input.size() > SHM_SIZE)
                return;

            strcpy(_addr, input.c_str());
        }
        notify(_wfd);
    }

    void read(std::string& output)
    {
        if (wait(_rfd))
        {
            output.clear();
            output = _addr;
        }
        notify(_wfd);
    }

private:
    int   _type;
    int   _shmid;
    char* _addr;
    int   _rfd;
    int   _wfd;
    bool _begin_to_write;
};
